// Copyright (c) 2005  INRIA Sophia-Antipolis (France).
// All rights reserved.
//
// This file is part of cgal-python; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; version 2.1 of the License.
// See the file LICENSE.LGPL distributed with cgal-python.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// $Id: Py_Min_circle_2.cpp 167 2007-11-02 15:36:54Z spion $
// $URL: svn+ssh://scm.gforge.inria.fr/svn/cgal-python/trunk/cgal-python/bindings/Geometric_Optimisation/Py_Min_circle_2.cpp $
//
// Author(s)     : Naceur Meskini
//=========================================================================

#include <CGAL/basic.h>
#include <include/Kernels.h>
#include <CGAL/Min_circle_2.h>
#include <CGAL/Min_circle_2_traits_2.h>

#include <include/iterator.h>
#include <boost/python.hpp>
#include <boost/cstdint.hpp>
#include <list>
#include <iostream>

#include "Py_Min_circle_2_doc.h"

using namespace boost::python;

namespace  {
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(is_valid_overloads_0_2, is_valid, 0, 2)
}


template < class Traits ,class kernel>
class My_Min_circle_2 : public CGAL::Min_circle_2<Traits>
{
	public:
		typedef typename CGAL::Min_circle_2<Traits> Min_circle_2;	
		typedef typename kernel::Point_2 Point_2;
		typedef typename std::vector<Point_2>::iterator iterator;
		typedef typename std::pair<iterator,iterator> range;
		typedef typename Min_circle_2::Point_iterator Point_iterator;
		typedef typename Min_circle_2::Support_point_iterator Support_point_iterator;

	public:
		My_Min_circle_2(const Traits& traits = Traits()):Min_circle_2(traits){};
		My_Min_circle_2(const boost::python::list& pts,bool randomize = false)
			:Min_circle_2(get_list(pts).begin(), get_list(pts).end(), randomize){}

		My_Min_circle_2(const Point_2& p,const Traits& traits = Traits()):Min_circle_2(p,traits){}
		My_Min_circle_2(Point_2 p,Point_2 q,const Traits& traits = Traits()):Min_circle_2(p,q,traits){}
		My_Min_circle_2(const Point_2& p,const Point_2& q,const Point_2& s,const Traits& traits = Traits()):Min_circle_2(p,q,s,traits){}
		bool is_valid( bool verbose = false, int level = 0) const
		{
			return this->Min_circle_2::is_valid(verbose,level);
		}
		void insert_list(const boost::python::list& pts)
		{
			int N = boost::python::extract<int>(pts.attr("__len__")());
			for(int i = 0 ; i < N ; i++)
			{
				Point_2 P = boost::python::extract<Point_2>(pts[i]); 
				this->insert(P);
			}
		}
		void insert(const Point_2& P){Min_circle_2::insert(P);}

		simple_python_iterator<Point_iterator> points()
		{
			std::pair< Point_iterator , Point_iterator > p( this->points_begin(), this->points_end() );
			return simple_python_iterator<Point_iterator>(p);
		}
		simple_python_iterator<Support_point_iterator> support_points()
		{
			std::pair< Support_point_iterator , Support_point_iterator > p( this->support_points_begin(), this->support_points_end() );
			return simple_python_iterator<Support_point_iterator>(p);
		}
	private:
		std::vector<Point_2> m_list;
		std::vector<Point_2>& get_list(const boost::python::list& pts)
		{
			//a segmentation fault is thrown when we give an empty list
			
			if (m_list.empty())
			{
				int N = boost::python::extract<int>(pts.attr("__len__")());
				for(int i = 0 ; i < N ; i++)
				{
					Point_2 P = boost::python::extract<Point_2>(pts[i]); 
					m_list.push_back(P);
				}
			}
			return m_list;
		}	
};


template<class kernel>
void Py_Min_circle_2()
{
	typedef typename kernel::Point_2 Point_2;
	typedef CGAL::Min_circle_2_traits_2<kernel> circle_2_trait;
	typedef My_Min_circle_2<circle_2_trait,kernel> Min_circle_2;
	typedef typename Min_circle_2::Point_iterator Point_iterator;
	typedef typename Min_circle_2::Support_point_iterator Support_point_iterator;

	typedef typename circle_2_trait::Circle Optimisation_circle_2;

    class_< Min_circle_2,boost::noncopyable >("Min_circle_2", Min_circle_2_doc,init< optional< const circle_2_trait& > >())
        .def(init< const Point_2&, optional< const circle_2_trait& > >(Min_circle_init_doc))
        .def(init< Point_2, Point_2, optional< const circle_2_trait& > >())
        .def(init< const Point_2&, const Point_2&, const Point_2&, optional< const circle_2_trait& > >())
      	.def(init<const boost::python::list&,optional<bool > >())
	.def("insert",&Min_circle_2::insert_list,Min_circle_insert_doc)
	.def("insert",(void  (Min_circle_2::*)(const Point_2&))&Min_circle_2::insert)
        .def("number_of_points", &Min_circle_2::number_of_points,Min_circle_number_of_points_doc)
        .def("number_of_support_points", &Min_circle_2::number_of_support_points,Min_circle_number_of_support_points_doc)
        .def("is_empty", &Min_circle_2::is_empty,Min_circle_is_empty_doc)
        .def("is_degenerate", &Min_circle_2::is_degenerate,Min_circle_is_degenerate_doc)
        .def("support_point", &Min_circle_2::support_point, return_value_policy< copy_const_reference >(),Min_circle_support_point_doc)
        .def("circle", &Min_circle_2::circle, return_value_policy< copy_const_reference >(),Min_circle_circle_doc)
        .def("bounded_side", &Min_circle_2::bounded_side,Min_circle_bounded_side_doc)
        .def("has_on_bounded_side", &Min_circle_2::has_on_bounded_side,Min_circle_has_on_bounded_side_doc)
        .def("has_on_boundary", &Min_circle_2::has_on_boundary,Min_circle_has_on_boundary_doc)
        .def("has_on_unbounded_side", &Min_circle_2::has_on_unbounded_side,Min_circle_has_on_unbounded_side_doc)
        .def("clear", &Min_circle_2::clear,Min_circle_clear_doc)
        .def("is_valid", &Min_circle_2::is_valid,is_valid_overloads_0_2(Min_circle_is_valid_doc))
	.add_property("points",&Min_circle_2::points)
	.add_property("support_points",&Min_circle_2::support_points)
    ;

	simple_python_iterator<Point_iterator>::declare("Point_iterator");
	simple_python_iterator<Support_point_iterator>::declare("Support_point_iterator");

    class_< Optimisation_circle_2 >("Optimisation_circle_2", init<  >())
        .def(init< const Optimisation_circle_2& >())
        .def("set", (void (Optimisation_circle_2::*)() )&Optimisation_circle_2::set)
        .def("set", (void (Optimisation_circle_2::*)(const Point_2&) )&Optimisation_circle_2::set)
        .def("set", (void (Optimisation_circle_2::*)(const Point_2&, const Point_2&) )&Optimisation_circle_2::set)
        .def("set", (void (Optimisation_circle_2::*)(const Point_2&, const Point_2&, const Point_2&) )&Optimisation_circle_2::set)
        .def("center", &Optimisation_circle_2::center, return_value_policy< copy_const_reference >())
        .def("squared_radius", &Optimisation_circle_2::squared_radius, return_value_policy< copy_const_reference >())
        .def("bounded_side", &Optimisation_circle_2::bounded_side)
        .def("has_on_bounded_side", &Optimisation_circle_2::has_on_bounded_side)
        .def("has_on_boundary", &Optimisation_circle_2::has_on_boundary)
        .def("has_on_unbounded_side", &Optimisation_circle_2::has_on_unbounded_side)
        .def("is_empty", &Optimisation_circle_2::is_empty)
        .def("is_degenerate", &Optimisation_circle_2::is_degenerate)
        .def( self == self )
        .def( self != self )
    ;
}

void export_Min_circle_2()
{
	Py_Min_circle_2<K>();
}
